package com.gator.spring.framework.beans.support;

import com.gator.spring.framework.beans.factory.config.PABLO_BeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Author PABLO
 * @Date 2022/5/7 12:19
 * @Desc definition读取器
 */
public class PABLO_BeanDefinitionReader {

    //保存所有需要注册的class
    private List<String> registryBeanClasses = new ArrayList<String>();

    private Properties config = new Properties();

    //固定配置文件中的key，相对于xml的规范
    private final String SCAN_PACKAGE = "scanPackage";

    /**
     * @Description: 将入参配置文件加载到内存
     * @Author: PABLO
     * @Date: 2022/5/7 12:21
     * @Params: [locations]
     * @Return:
     **/
    public PABLO_BeanDefinitionReader(String... locations) {

        //通过URL定位找到其所对应的文件，然后转换为文件流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(locations[0].replace("classpath:", ""));
        try {
            //加载配置
            config.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //多路径扫描
        String property = config.getProperty(SCAN_PACKAGE);
        for (String scanPackage : property.split(",")) {
            doScanner(scanPackage);
        }

    }

    private void doScanner(String scanPackage) {
        //转换为文件路径，实际上就是把.替换为/就OK了
        //处理每个包，将包路径转为文件路径  .-->/
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        //System.out.println("url" + url);
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            //如果是文件夹，继续递归
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                //E:\IDEALocation\giant-gator\target\classes\com\bj\summary\spring_mvc\controller\TestController.class
                registryBeanClasses.add(scanPackage + "." + file.getName().replace(".class", "").trim());
            }
        }
    }

    public Properties getConfig() {
        return this.config;
    }


    /**
     * @Description: 配置信息封装为definition规范
     *              注意：我这里定义的规则是：(可自定义)
     *                  如果是实现类，可使用实现类完全限定名/实现类首字母小写/接口完全限定名/
     *                  如果是普通类，可使用类的完全限定名/类名小写getBean获取对象
     * @Author: PABLO
     * @Date: 2022/5/7 12:29
     * @Params: []
     * @Return: java.util.List<GPBeanDefinition>
     **/
    public List<PABLO_BeanDefinition> loadBeanDefinitions() {

        List<PABLO_BeanDefinition> result = new ArrayList<PABLO_BeanDefinition>();
        try {
            for (String className : registryBeanClasses) {
                Class<?> beanClass = Class.forName(className);
                //如遇接口使用其实现类初始化
                if (beanClass.isInterface()) continue;
                //beanName有三种情况:
                //1、默认是类名首字母小写
                //2、自定义名字
                //3、接口注入
                //判断首字母大小写，如小写直接添加，如大写需转换
                //beanClass.getSimpleName()是类名
                //beanClass.getName()是类的完全限定名
                //保存小写名称的映射关系
                char c = beanClass.getSimpleName().toCharArray()[0];
                result.add(doCreateBeanDefinition(Character.isLowerCase(c)
                                ? beanClass.getSimpleName()
                                : toLowerFirstCase(beanClass.getSimpleName()),
                        beanClass.getName()));
                //保存完全限定名的映射关系
                result.add(doCreateBeanDefinition(beanClass.getName(), beanClass.getName()));
                //获取该实现类的接口列表
                Class<?>[] interfaces = beanClass.getInterfaces();
                for (Class<?> i : interfaces) {
                    //如果是多个实现类，只能覆盖
                    //为什么？因为Spring没那么智能，就是这么傻
                    //这个时候，可以自定义名字
                    //保存接口的完全限定名的映射关系
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @Description: 将配置信息解析为definition规范
     * @Author: PABLO
     * @Date: 2022/5/7 12:31
     * @Params: [factoryBeanName, beanClassName 是完全限定名]
     * @Return: GPBeanDefinition
     **/
    private PABLO_BeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName) {

        PABLO_BeanDefinition beanDefinition = new PABLO_BeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(factoryBeanName);
        //System.out.println(beanDefinition);
        return beanDefinition;
    }

    /**
     * @Description:大写转小写
     * @Author: PABLO
     * @Date: 2022/5/7 12:34
     * @Params: [simpleName]
     * @Return: java.lang.String
     **/
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        //大小写字母的ASCII码相差32，
        //而且大写字母的ASCII码要小于小写字母的ASCII码
        //在Java中，对char做算学运算，实际上就是对ASCII码做算学运算
        chars[0] += 32;
        return String.valueOf(chars);
    }

}
